/*
 * PhonemapAPI.java.
 */
package com.phonemap.service.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

import org.json.JSONArray;
import org.json.JSONObject;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.search.GeoPoint;
import com.phonemap.common.AppConstant;
import com.phonemap.common.Util;
import com.phonemap.dto.PositionDTO;
import com.phonemap.math.LatLng;
import com.phonemap.models.Position;
import com.phonemap.models.User;
import com.phonemap.utils.PhonemapUtil;

/**
 * The Class PhonemapAPI.
 * 
 * @author Administrator
 */
@WebService
public class PhonemapAPI {

	/** The Constant logger. */
	private static final Logger logger = Logger.getLogger(PhonemapAPI.class
			.getCanonicalName());

	/**
	 * Insert user.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @param link
	 *            the link
	 * @param birthday
	 *            the birthday
	 * @param interestedIn
	 *            the interested in
	 * @param relationshipStatus
	 *            the relationship status
	 * @param gender
	 *            the gender
	 * @param email
	 *            the email
	 * @param timezone
	 *            the timezone
	 * @param locale
	 *            the locale
	 * @param verified
	 *            the verified
	 * @param updatedTime
	 *            the updated time
	 * @param username
	 *            the username
	 * @param latitude
	 *            the latitude
	 * @param longitude
	 *            the longitude
	 * @param locatedTime
	 *            the located time
	 * @param phone
	 *            the phone
	 * @return the string
	 */
	@WebMethod
	public String insertUser(
			@WebParam(name = "id", targetNamespace = "") long id,
			@WebParam(name = "fullname", targetNamespace = "") String fullname,
			@WebParam(name = "link", targetNamespace = "") String link,
			@WebParam(name = "birthday", targetNamespace = "") Date birthday,
			@WebParam(name = "interested_in", targetNamespace = "") String interestedIn,
			@WebParam(name = "relationship_status", targetNamespace = "") String relationshipStatus,
			@WebParam(name = "gender", targetNamespace = "") String gender,
			@WebParam(name = "email", targetNamespace = "") String email,
			@WebParam(name = "timezone", targetNamespace = "") String timezone,
			@WebParam(name = "locale", targetNamespace = "") String locale,
			@WebParam(name = "verified", targetNamespace = "") String verified,
			@WebParam(name = "updated_time", targetNamespace = "") String updatedTime,
			@WebParam(name = "username", targetNamespace = "") String username,
			@WebParam(name = "latitude", targetNamespace = "") float latitude,
			@WebParam(name = "longitude", targetNamespace = "") float longitude,
			@WebParam(name = "located_time", targetNamespace = "") Date locatedTime,
			@WebParam(name = "phone", targetNamespace = "") String phone) {
		logger.log(Level.INFO, "inserting user");

		Entity user = User.insertUser(id, fullname, link, birthday,
				interestedIn, gender, email, relationshipStatus, timezone,
				locale, verified, updatedTime, username, null, new GeoPt(
						latitude, longitude), locatedTime, phone);
		if (user != null) {
			logger.log(Level.INFO, "inserting user:" + id);
			return Util.writeWSJSON(user, AppConstant.SUCCESS_STATUS,
					AppConstant.SUCCESS_INSERT);
		}
		return Util.writeWSJSON(user, AppConstant.FAIL_STATUS,
				AppConstant.NO_SUCCESS_INSERT);
	}

	/**
	 * Update user.
	 * 
	 * @param id
	 *            the id
	 * @param fullname
	 *            the fullname
	 * @param link
	 *            the link
	 * @param birthday
	 *            the birthday
	 * @param interestedIn
	 *            the interested in
	 * @param relationshipStatus
	 *            the relationship status
	 * @param gender
	 *            the gender
	 * @param email
	 *            the email
	 * @param timezone
	 *            the timezone
	 * @param locale
	 *            the locale
	 * @param verified
	 *            the verified
	 * @param updatedTime
	 *            the updated time
	 * @param username
	 *            the username
	 * @param latitude
	 *            the latitude
	 * @param longitude
	 *            the longitude
	 * @param locatedTime
	 *            the located time
	 * @param phone
	 *            the phone
	 * @return the string
	 */
	@WebMethod
	public String updateUser(
			@WebParam(name = "id", targetNamespace = "") long id,
			@WebParam(name = "fullname", targetNamespace = "") String fullname,
			@WebParam(name = "link", targetNamespace = "") String link,
			@WebParam(name = "birthday", targetNamespace = "") Date birthday,
			@WebParam(name = "interested_in", targetNamespace = "") String interestedIn,
			@WebParam(name = "relationship_status", targetNamespace = "") String relationshipStatus,
			@WebParam(name = "gender", targetNamespace = "") String gender,
			@WebParam(name = "email", targetNamespace = "") String email,
			@WebParam(name = "timezone", targetNamespace = "") String timezone,
			@WebParam(name = "locale", targetNamespace = "") String locale,
			@WebParam(name = "verified", targetNamespace = "") String verified,
			@WebParam(name = "updated_time", targetNamespace = "") String updatedTime,
			@WebParam(name = "username", targetNamespace = "") String username,
			@WebParam(name = "latitude", targetNamespace = "") float latitude,
			@WebParam(name = "longitude", targetNamespace = "") float longitude,
			@WebParam(name = "located_time", targetNamespace = "") Date locatedTime,
			@WebParam(name = "phone", targetNamespace = "") String phone) {
		logger.log(Level.INFO, "updating user");
		Entity user = User.updateUser(id, fullname, link, birthday,
				interestedIn, gender, email, relationshipStatus, timezone,
				locale, verified, updatedTime, username, new GeoPt(latitude,
						longitude), locatedTime, phone);
		if (user != null) {
			logger.log(Level.INFO, "updating user:" + id);
			return Util.writeWSJSON(user, AppConstant.SUCCESS_STATUS,
					AppConstant.SUCCESS_UPDATE);
		}
		return Util.writeWSJSON(user, AppConstant.FAIL_STATUS,
				AppConstant.NO_SUCCESS_UPDATE);
	}

	/**
	 * Update position.
	 * 
	 * @param id
	 *            the id
	 * @param latitude
	 *            the latitude
	 * @param longitude
	 *            the longitude
	 * @param locatedTime
	 *            the located time
	 * @return the string
	 */
	@WebMethod
	public String updatePosition(
			@WebParam(name = "id", targetNamespace = "") long id,
			@WebParam(name = "latitude", targetNamespace = "") float latitude,
			@WebParam(name = "longitude", targetNamespace = "") float longitude,
			@WebParam(name = "located_time", targetNamespace = "") Date locatedTime) {
		logger.log(Level.INFO, "updating position");
		Entity user = null;
		user = User.updatePosition(id, new GeoPt(latitude, longitude),
				locatedTime);
		if (user != null) {
			return Util.writeWSJSON(user, AppConstant.SUCCESS_STATUS,
					AppConstant.SUCCESS_UPDATE);
		}
		return Util.writeWSJSON(user, AppConstant.FAIL_STATUS,
				AppConstant.NO_SUCCESS_UPDATE);
	}

	/**
	 * Gets the user.
	 * 
	 * @param id
	 *            the id
	 * @return the user
	 */
	@WebMethod
	public String getUser(@WebParam(name = "id", targetNamespace = "") long id) {
		logger.log(Level.INFO, "retrieving user");
		// FIXME - add try catch
		Entity user = null;
		user = User.getUser(id);
		if (user != null) {
			return Util.writeWSJSON(user, AppConstant.SUCCESS_STATUS,
					AppConstant.EMPTY_STRING);
		}
		return Util.writeWSJSON(user, AppConstant.SUCCESS_STATUS,
				AppConstant.NO_DATA);
	}

	/**
	 * Delete user.
	 * 
	 * @param id
	 *            the id
	 * @return the string
	 */
	@WebMethod
	public String deleteUser(
			@WebParam(name = "id", targetNamespace = "") long id) {
		logger.log(Level.INFO, "deleting user");
		String message = User.deleteUser(id);
		Entity entity = null;
		if (message.equalsIgnoreCase(AppConstant.SUCCESS_STATUS)) {
			return Util
					.writeWSJSON(entity, AppConstant.SUCCESS_STATUS, message);
		}
		return Util.writeWSJSON(entity, AppConstant.FAIL_STATUS, message);
	}

	/**
	 * Delete positions.
	 * 
	 * @param id
	 *            the id
	 * @param userId
	 *            the user id
	 * @return the string
	 */
	@WebMethod
	public String deletePositions(
			@WebParam(name = "id", targetNamespace = "") long id,
			@WebParam(name = "userId", targetNamespace = "") long userId) {
		logger.log(Level.INFO, "deleting position");
		String message = Position.deletePosition(id, userId);
		Entity entity = null;
		if (message.equalsIgnoreCase(AppConstant.SUCCESS_STATUS)) {
			return Util
					.writeWSJSON(entity, AppConstant.SUCCESS_STATUS, message);
		}
		return Util.writeWSJSON(entity, AppConstant.FAIL_STATUS, message);
	}

	/**
	 * Gets the positions.
	 * 
	 * @param userId
	 *            the user id
	 * @return the positions
	 */
	@WebMethod
	public String getPositions(
			@WebParam(name = "userId", targetNamespace = "") long userId) {
		logger.log(Level.INFO, "retrieving positions by userid " + userId);
		Iterable<Entity> positions = Position.getPositions(userId);
		return Util.writeWSJSON(positions, AppConstant.FAIL_STATUS,
				AppConstant.EMPTY_STRING);
	}

	/**
	 * Gets the users by name.
	 * 
	 * @param name
	 *            the name
	 * @param offset
	 *            the offset
	 * @return the users by name
	 */
	@WebMethod
	public String getUsersByName(
			@WebParam(name = "name", targetNamespace = "") String name,
			@WebParam(name = "offset", targetNamespace = "") int offset) {
		logger.log(Level.INFO, "retrieving user");
		String status = AppConstant.EMPTY_STRING;
		String msg = AppConstant.EMPTY_STRING;
		List<Entity> entities = new ArrayList<>();
		try {

			entities = User.getUsers(name, offset);
			if (entities == null || entities.isEmpty()) {
				msg = AppConstant.NO_DATA;
			}
			status = AppConstant.SUCCESS_STATUS;
		} catch (Exception e) {
			msg = e.getMessage();
			status = AppConstant.FAIL_STATUS;
		}
		return Util.writeWSJSON(entities, status, msg);
	}

	/**
	 * Gets the history.
	 * 
	 * @param userId
	 *            the user id
	 * @param days
	 *            the days
	 * @param offset
	 *            the offset
	 * @return the history
	 */
	@WebMethod
	public String getHistory(
			@WebParam(name = "user_id", targetNamespace = "") long userId,
			@WebParam(name = "days", targetNamespace = "") int days,
			@WebParam(name = "offset", targetNamespace = "") int offset) {
		String status = AppConstant.EMPTY_STRING;
		String msg = AppConstant.EMPTY_STRING;
		List<PositionDTO> entities = new ArrayList<PositionDTO>();
		try {

			entities = Position.getHistory(userId, days, offset);
			if (entities == null || entities.isEmpty()) {
				msg = AppConstant.NO_DATA;
			}
			status = AppConstant.SUCCESS_STATUS;
		} catch (Exception e) {
			msg = e.getMessage();
			status = AppConstant.FAIL_STATUS;
		}
		return Util.writePositionJSON(entities, status, msg);
	}

	/**
	 * Gets the users by meter.
	 * 
	 * @param latitude
	 *            the latitude
	 * @param longitude
	 *            the longitude
	 * @param meters
	 *            the meters
	 * @param offset
	 *            the offset
	 * @return the users by meter
	 */
	@WebMethod
	public String getUsersByMeter(
			@WebParam(name = "latitude", targetNamespace = "") float latitude,
			@WebParam(name = "longitude", targetNamespace = "") float longitude,
			@WebParam(name = "meters", targetNamespace = "") int meters,
			@WebParam(name = "offset", targetNamespace = "") int offset) {
		String status = AppConstant.EMPTY_STRING;
		String msg = AppConstant.EMPTY_STRING;
		List<Entity> entities = new ArrayList<Entity>();
		try {
			GeoPoint position = new GeoPoint(latitude, longitude);
			entities = User.getUsers(position, meters, offset);
			if (entities == null || entities.isEmpty()) {
				msg = AppConstant.NO_DATA;
			}
			status = AppConstant.SUCCESS_STATUS;
		} catch (Exception e) {
			msg = e.getMessage();
			status = AppConstant.FAIL_STATUS;
		}
		return Util.writeJSON(entities, status, msg);
	}

	
	/**
	 * Insert history.
	 *
	 * @param postionsJSON the postions json
	 * @return the string
	 */
	@WebMethod
	public String insertHistory(
			@WebParam(name = "postionsJSON", targetNamespace = "") String postionsJSON) {
		// "{"
//				 + "  \"data\": ["
//				 + "    {"
//				 + "      \"user_id\": \"1\","
//				 + "      \"latitude\": \"10.1\","
//				 + "      \"longitude\": \"103.4\","
//				 + "      \"located_time\": \"1970-01-01T14:00:00.000Z\","
//				 + "    },"
//				 + "    {"
//				 + "      \"id\": \"2\","
//				 + "      \"latitude\": \"10.1\""
//				 + "    }"
//				 + "  ]"
//				 + "}";
				//
		String status = AppConstant.EMPTY_STRING;
		String msg = AppConstant.EMPTY_STRING;
		try {
			
			final JSONObject obj = new JSONObject(postionsJSON);
			final JSONArray geodata = obj.getJSONArray("data");
			
			if (geodata.length() > 0) {
				int len = geodata.length();
				JSONObject pos = geodata.getJSONObject(0);
				PositionDTO dto = new PositionDTO(pos);
				LatLng start = dto.getPosition();
				// Insert first point
				Position.insertPosition(pos);
				for (int i = 1; i < len - 1; i++) {
					pos = geodata.getJSONObject(i);
					dto = new PositionDTO(pos);
					LatLng mid = dto.getPosition();
					pos = geodata.getJSONObject(i + 1);
					dto = new PositionDTO(pos);
					LatLng end = dto.getPosition();
					if (!PhonemapUtil.isInside(mid, start, end)) {
						Position.insertPosition(pos);
					}
				}
				if (len - 1 > 0) {
					//Insert the last point
					Position.insertPosition(geodata.getJSONObject(len - 1));
				}
			} else {
				msg = "No data inserted.";
			}
				
			status = AppConstant.SUCCESS_STATUS;
		} catch (Exception e) {
			msg = e.getMessage();
		}
		return Util.writeJSON(null, status, msg);
	}

}
